/* Copyright 2011 Google Inc. All Rights Reserved.
 * Author: markoi@google.com (Marko Ivankovic)
 *
 * This is a small example aplication for the Game Jam event that was held in
 * October 2011 in Zurich. The game was insipired by the old Achtung, die Kurve!
 * game developed by Filip Oscadal.
 *
 * This example is using the GameJS library.
 */

// Closure-style imports.

var $gamejs = require('gamejs');
var $draw = require('gamejs/draw');

// Constants
var SCREEN_WIDTH = 800, SCREEN_HEIGHT = 600;
var PX_PER_M = 15;
var SPEED = 3, ANGULAR_SPEED = 3;

// Because the user input is asynchronous to the game loop, we will use this
// dictionary to track the state of the user input.
var keys = {};
// The game state.
var used_space = {};

function enc(pos) {
  // This is a very very very cheap trick to get collision done.
  // It works most of the time, but not always.
  return Math.floor(pos[0] / 2) + " " + Math.floor(pos[1] / 2);
}

// This will be a class extending the GameJS sprite class. It represents a
// single player instance, with the position, angle, color and key binding.
var Player = function(params) {
  Player.superConstructor.apply(this, arguments);

  this.pos = params.pos;
  this.angle = 0;
  this.color = params.color;
  this.keys = params.keys;

  this.hit = false;

  return this;
}
// Extending the Sprite class obliges us to implement update and draw methods.
$gamejs.utils.objects.extend(Player, $gamejs.sprite.Sprite);

// The update method is called in each cycle. It should update the game state of
// this particular entitiy. For us, this means move one step from the current
// position in the current angle and than check if we hit something.
// This is the right place to react to user input caught outside the loop. We
// check the status of the keys variable and adjust accordingly.
Player.prototype.update = function(msDuration) {
  // If we are done, we are done.
  if (this.hit)
    return;

  // Move
  this.pos[0] = this.pos[0] + Math.cos(this.angle * Math.PI / 180.0) * SPEED;
  this.pos[1] = this.pos[1] + Math.sin(this.angle * Math.PI / 180.0) * SPEED;

  // Did we hit the frame or a snake?
  if ((this.pos[0] < 1 || this.pos[0] > SCREEN_WIDTH) ||
      (this.pos[1] < 1 || this.pos[1] > SCREEN_HEIGHT) ||
      (used_space[enc(this.pos)])) {
        this.hit = true;
  }
  // Mark this position as visited.
  used_space[enc(this.pos)] = true;

  // React to key state.
  if (keys[this.keys['cw']]) {
    this.angle += ANGULAR_SPEED;
    if (this.angle < 0) this.angle += 360;
  }
  if (keys[this.keys['ccw']]) {
    this.angle -= ANGULAR_SPEED;
    if (this.angle > 360) this.angle -= 360;
  }
}

// Draw the sprite on the surface.
Player.prototype.draw = function(surface) {
  // If we are not dead...
  if (!this.hit) {
    // ...we are a small circle. Note that the surface does NOT get cleared
    // automatically!
    $draw.circle(surface, this.color, this.pos, 2);
  }
}

// This is the standard way of executing code once the page fully loads.
$gamejs.ready(function() {
    // Set the GameJS display. This attaches the GameJS to the canvas defined in
    // the HTML.
    var display = $gamejs.display.setMode([SCREEN_WIDTH, SCREEN_HEIGHT]);
    $gamejs.display.setCaption("Example game");

    // Return the surface element.
    var mainsurface = $gamejs.display.getSurface();

    // Define 2 players, red and blue.
    var players = [
                   new Player({pos: [100, 100],
                               color: "#FF0000",
                               keys: {
                                 'cw': $gamejs.event.K_s,
                                 'ccw': $gamejs.event.K_a
                               }
                             }),
                   new Player({pos: [400, 400],
                               color: "#0000FF",
                               keys: {
                                 'cw': $gamejs.event.K_RIGHT,
                                 'ccw': $gamejs.event.K_LEFT
                               }
                             }),
                  ];

    // This function will be the primary loop.
    var tick = function(msDuration) {
      // Events are emitted async. This GameJS construct will loop over all
      // events emitted between the last two ticks of the game. For key events
      // we set the appropriate key state.

      $gamejs.event.get().forEach(function(event) {
        if (event.type === $gamejs.event.KEY_DOWN) {
          keys[event.key] = true;
        } else if (event.type === $gamejs.event.KEY_UP) {
          keys[event.key] = false;
        }
      });

      // This is the common way to handle the primary loop.
      // First update the entire game state.
      for (var i = 0; i < players.length; i++) {
        players[i].update(msDuration);
      }
      // Then draw the game state to the surface.
      for (var i = 0; i < players.length; i++) {
        players[i].draw(mainsurface);
      }
      return;
    };

    // Register the primary loop, 50 fps.
    $gamejs.time.fpsCallback(tick, this, 50);
});
